
from django import forms
from django.http import HttpResponseRedirect
from django.contrib.contenttypes.models import ContentType

from customtags.decorators import block
from tagging.models import Tag, TaggedItem
from tagging.forms import TagField
from pirate_core import FormMixin, namespace_get
from django import template

from customtags.decorators import block_decorator
register = template.Library()
block = block_decorator(register)

# this function assignment lets us reuse the same code block a bunch of places
get_namespace = namespace_get('pp_tags')


@block
def pp_get_tagged_objects(context, nodelist, *args, **kwargs):
    """
    Creates a queryset that is a subset of the specified queryset that contains only
    objects tagged with at least one of the specified tags.

    In this way, the tag can be invoked as follows:

    {% pp_get_tagged_objects queryset=pp_issue.issues tags="tax taxes" range=request.range %}
       Do stuff with {{ pp_tags.tagged_objects }}
    {% endpp_get_tagged_objects %}

    '''
    '''
    
    >>> from django import template
    >>> from pirate_issues.models import Issue, Topic
    >>> from tagging.models import Tag

    Setup...
    >>> template.add_to_builtins('customtags.templatetags.customtags')

    >>> topic = Topic(text="Opinions and Views.")
    >>> topic.save()
    >>> issue1 = Issue(name="My opinion on gun control.", topic=topic)
    >>> issue1.save()
    >>> Tag.objects.update_tags(issue1, "congress")
    >>> issue2 = Issue(name="My opinion on tax cuts.", topic=topic)
    >>> issue2.save()
    >>> Tag.objects.update_tags(issue2, "taxes congress")

    Experimental control...
    >>> ts = '''
    ...      {% spaceless %}
    ...      {% for obj in qs %}
    ...         {{obj.name}},
    ...      {% endfor %}
    ...      {% endspaceless %}
    ...      '''

    >>> dt = { "qs" : Issue.objects.all() }
    >>> result = template.Template(ts).render(template.Context(dt))
    >>> " ".join(result.strip().split())
    u'My opinion on gun control., My opinion on tax cuts.,'

    Tests 1 & 2...
    >>> ts = '''
    ...      {% spaceless %}
    ...      {% pp_get_tagged_objects queryset=qs tags="congress" %}
    ...         {% for obj in pp_tags.tagged_objects %}
    ...            {{obj.name}},
    ...         {% endfor %}
    ...      {% endpp_get_tagged_objects %}
    ...      {% endspaceless %}
    ...      '''

    >>> dt = { "qs" : Issue.objects.all() }
    >>> result1 = template.Template(ts).render(template.Context(dt))
    >>> " ".join(result1.strip().split())
    u'My opinion on gun control., My opinion on tax cuts.,'

    >>> ts = '''
    ...      {% spaceless %}
    ...      {% pp_get_tagged_objects queryset=qs tags="taxes" %}
    ...         {% for obj in pp_tags.tagged_objects %}
    ...            {{obj.name}},
    ...         {% endfor %}
    ...      {% endpp_get_tagged_objects %}
    ...      {% endspaceless %}
    ...      '''

    >>> dt = { "qs" : Issue.objects.all() }
    >>> result2 = template.Template(ts).render(template.Context(dt))
    >>> result2.strip()
    u'My opinion on tax cuts.,'

    Tear down...
    >>> Tag.objects.all().delete()
    >>> issue1.delete()
    >>> issue2.delete()
    >>> topic.delete()
    """
    context.push()
    namespace = get_namespace(context)

    queryset = kwargs.pop('queryset', None)
    rng      = kwargs.pop('range', None)
    tags     = kwargs.pop('tags', None)

    if isinstance(rng, basestring):
        try:
            rng = rng.strip("()").split(",")
            rng = (int(rng[0]), int(rng[1]))
        except:
            raise ValueException("The argument 'rng=' to pp_get_tagged_objects must be "
                                 "provided either in the form of an (int, int) pair, or "
                                 "as a string in the form of '(<int>, <int>)'.")

    elif rng is not None and len(rng) != 2 and \
         (not isinstance(rng[0], int) or not isinstance(rng[1], int)):
        raise ValueException("The argument 'rng=' to pp_get_tagged_objects must be "
                             "provided either in the form of an (int, int) pair, or "
                             "as a string in the form of '(<int>, <int>)'.")

    if tags is None:
        raise ValueException("A list of tags must be specified for pp_get_tagged_objects.")

    if queryset is None:
        raise ValueException("A list of tags must be specified for pp_get_tagged_objects.")

    if rng:
        new_queryset = TaggedItem.objects.get_union_by_model(queryset, tags)[rng[0]:rng[1]]
    else:
        new_queryset = TaggedItem.objects.get_union_by_model(queryset, tags)

    namespace['tagged_objects'] = new_queryset
    output = nodelist.render(context)
    context.pop()

    return output
        

@block
def pp_tag_form(context, nodelist, *args, **kwargs):
    '''
    This block tag can create or process forms either to create or to modify issues.
    Usage is as follows:

    {% pp_tag_form POST=request.POST path=request.path object=pp_issue.issue %}
       Do stuff with {{ pp-issue.form }}.
    {% endpp_issue_form %}

    >>> from django import template
    >>> from django.test.client import Client
    >>> from pirate_issues.models import Issue, Topic
    >>> from pirate_extensions.templatetags.taggingtags import TagForm
    >>> from tagging.models import Tag

    Setup...
    >>> topic = Topic(text="Opinions and Views.")
    >>> topic.save()
    >>> issue = Issue(name="My view regarding health care.", topic=topic)
    >>> issue.save()

    Get the path for the form to update...
    >>> path_ts = "{% load pp_url %}{% pp_url template='test_add_tags.html' object=obj %}"
    >>> path_dt = { "obj" : issue }
    >>> path = template.Template(path_ts).render(template.Context(path_dt))

    Run the test...
    >>> data = { "form_id" : TagForm.form_id(issue), "tags" : "taxes congress", }
    >>> c = Client()
    >>> c.post(path, data)
    <django.http.HttpResponseRedirect object at ...>
    >>> tags = Tag.objects.get_for_object(issue)
    >>> tags
    [<Tag: congress>, <Tag: taxes>]

    Teardown...
    >>> Tag.objects.all().delete()
    >>> issue.delete()
    >>> topic.delete()
    '''

    context.push()
    namespace = get_namespace(context)

    POST    = kwargs.get('POST', None)
    path    = kwargs.get('path', None) 
    obj     = kwargs.get('object', None)
    as_name = kwargs.get('as_name', None)

    form = TagForm.create(POST, path, obj)

    if as_name is not None:
        context[asname] = form
    else:
        namespace['form'] = form

    output = nodelist.render(context)
    context.pop()

    return output


class TagForm(forms.Form, FormMixin):
    tags = TagField()

    def __init__(self, POST=None, instance=None):
        self.instance = instance 
        super(TagForm, self).__init__(POST)

    def clean(self):
        if not self.instance:
            raise ValidationError("A Tag Form requires that a tagged object be specified.")
        return super(TagForm, self).clean()

    def save(self, commit=True):
        if commit:
            Tag.objects.update_tags(self.instance, self.cleaned_data['tags'])


